package com.study.retrofitrealize;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import okhttp3.Call;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;

/**
 * 作者： zjf 10/7/20 12:01 PM
 * 参考：
 * 描述：
 */
public class MyRetrofit {

    //基础url
    final HttpUrl baseUrl;
    //OkHttpClient实现了Call.Factory
    final Call.Factory callFactory;
    //缓存解析过得方法
    final Map<Method, ClassMethod> methodCache = new ConcurrentHashMap<>();

    /**
     * 构造方法
     */
    public MyRetrofit(HttpUrl baseUrl, Call.Factory callFactory) {
        this.baseUrl = baseUrl;
        this.callFactory = callFactory;
    }


    /**
     * 构造器
     */
    public static class Builder {
        HttpUrl baseUrl;
        Call.Factory callFactory;

        public Builder callFactory(Call.Factory factory) {
            this.callFactory = factory;
            return this;
        }

        public Builder baseUrl(String url) {
            this.baseUrl = HttpUrl.get(url);
            return this;
        }


        public MyRetrofit build() {
            return new MyRetrofit(baseUrl, callFactory == null ? new OkHttpClient() : callFactory);
        }
    }

    /**
     * 创建接口访问实体类
     */
    public <T> T create(Class<T> tClass) {
        //返回代理对象实例
        return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //解析方法上的注解
                ClassMethod classMethod = loadClassMethod(method);

                //返回代理对象方法的返回值
                return classMethod.invoke(args);
            }
        });
    }

    private ClassMethod loadClassMethod(Method method) {
        ClassMethod classMethod = methodCache.get(method);
        //如果缓存有解析过得方法,直接返回
        if (classMethod != null) {
            return classMethod;
        }
        //避免多线程操作,破坏原子性
        synchronized (methodCache) {
            classMethod = methodCache.get(method);
            //同步锁双重检验
            if (classMethod == null) {
                classMethod = new ClassMethod.Builder(this, method).build();
                methodCache.put(method, classMethod);
            }
        }
        return classMethod;
    }
}
